package com.googlecode.nauka.mydbplugin.schemaview;

import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.Map;

import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.DrillDownAdapter;
import org.eclipse.ui.part.ViewPart;

import com.googlecode.nauka.dbfactory.DBFactory;
import com.googlecode.nauka.mydbplugin.editor.SQLEditor;
import com.googlecode.nauka.mydbplugin.editor.SQLEditorInput;

public class TablesView extends ViewPart{
    public static final String ID = TablesView.class.getName();
    private TreeViewer         viewer;
    private DrillDownAdapter   drillDownAdapter;
    private Action             doubleClickAction;
    private String             userName;
    private Action             refresh;

    class TreeObject implements IAdaptable{
        private String     name;
        private TreeParent parent;

        public TreeObject(String name){
            this.name = name;
        }

        public String getName(){
            return name;
        }

        public void setParent(TreeParent parent){
            this.parent = parent;
        }

        public TreeParent getParent(){
            return parent;
        }

        public String toString(){
            return getName();
        }

        @SuppressWarnings("unchecked")
        public Object getAdapter(Class key){
            return null;
        }
    }

    class TreeParent extends TreeObject{
        private ArrayList<TreeObject> children;

        public TreeParent(String name){
            super(name);
            children = new ArrayList<TreeObject>();
        }

        public TreeObject getChild(String name){
            for(TreeObject c: children){
                if(c.getName().equals(name))
                    return c;
            }
            TreeParent o = new TreeParent(name);
            addChild(o);
            return o;
        }

        public void addChild(TreeObject child){
            children.add(child);
            child.setParent(this);
        }

        public void removeChild(TreeObject child){
            children.remove(child);
            child.setParent(null);
        }

        public TreeObject[] getChildren(){
            return (TreeObject[])children.toArray(new TreeObject[children
                .size()]);
        }

        public boolean hasChildren(){
            return children.size() > 0;
        }
    }

    class ViewContentProvider implements IStructuredContentProvider,
        ITreeContentProvider{
        private TreeParent invisibleRoot;

        public void inputChanged(Viewer v, Object oldInput, Object newInput){}

        public void dispose(){}

        public Object[] getElements(Object parent){
            if(parent.equals(getViewSite())){
                if(invisibleRoot == null)
                    try{
                        initialize();
                    }catch(Exception e){
                        e.printStackTrace();
                    }
                try{
                    read();
                }catch(Exception e){
                    e.printStackTrace();
                }
                return getChildren(invisibleRoot);
            }
            return getChildren(parent);
        }

        public Object getParent(Object child){
            if(child instanceof TreeObject){
                return ((TreeObject)child).getParent();
            }
            return null;
        }

        public Object[] getChildren(Object parent){
            if(parent instanceof TreeParent){
                return ((TreeParent)parent).getChildren();
            }
            return new Object[0];
        }

        public boolean hasChildren(Object parent){
            if(parent instanceof TreeParent)
                return ((TreeParent)parent).hasChildren();
            return false;
        }

        private void initialize() throws Exception{
            DBFactory
                .setDb(new DBFactory(
                    "C:/projekty/workspaces/lookas/MyDBExplorer/lib/derby.jar",
                    "org.apache.derby.jdbc.EmbeddedDriver",
                    "jdbc:derby:C:/projekty/workspaces/lookas/MyDBExplorer/derby.db;create=true"));
        }

        private void read() throws Exception{
            ResultSet set;
            userName = DBFactory.getDb().getConnection().getMetaData()
                .getUserName();
            Map<String, TreeParent> iSchemas = new LinkedHashMap<String, TreeParent>();
            TreeParent iSchema = new TreeParent("Schemas");
            set = DBFactory.getDb().getConnection().getMetaData().getSchemas();
            while(set.next()){
                TreeParent o = new TreeParent(set.getString("TABLE_SCHEM"));
                iSchema.addChild(o);
                iSchemas.put(o.getName(), o);
            }
            set.close();

            TreeParent iView = new TreeParent("Views");
            TreeParent iTable = new TreeParent("Tables");
            set = DBFactory.getDb().getConnection().getMetaData().getTables(
                null, null, "%", new String[]{"TABLE", "VIEW"});
            while(set.next()){
                TreeObject o1 = new TreeObject(set.getString("TABLE_NAME"));
                TreeObject o2 = new TreeObject(set.getString("TABLE_SCHEM")
                    + "." + set.getString("TABLE_NAME"));
                TreeParent o = iSchemas.get(set.getString("TABLE_SCHEM"));
                if(set.getString("TABLE_TYPE").equals("VIEW")){
                    ((TreeParent)o.getChild("Views")).addChild(o1);
                    iView.addChild(o2);
                }else
                    if(set.getString("TABLE_TYPE").equals("TABLE")){
                        ((TreeParent)o.getChild("Tables")).addChild(o1);
                        iTable.addChild(o2);
                    }
            }
            set.close();

            invisibleRoot = new TreeParent("");
            invisibleRoot.addChild(iSchema);
            invisibleRoot.addChild(iTable);
            invisibleRoot.addChild(iView);
        }
    }

    class ViewLabelProvider extends LabelProvider{

        public String getText(Object obj){
            return obj.toString();
        }

        public Image getImage(Object obj){
            String imageKey = ISharedImages.IMG_OBJ_ELEMENT;
            if(obj instanceof TreeParent)
                imageKey = ISharedImages.IMG_OBJ_FOLDER;
            return PlatformUI.getWorkbench().getSharedImages().getImage(
                imageKey);
        }
    }

    public TablesView(){}

    public void createPartControl(Composite parent){
        viewer = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
        drillDownAdapter = new DrillDownAdapter(viewer);
        viewer.setContentProvider(new ViewContentProvider());
        viewer.setLabelProvider(new ViewLabelProvider());
        viewer.setSorter(new ViewerSorter());
        viewer.setInput(getViewSite());
        makeActions();
        hookContextMenu();
        hookDoubleClickAction();
        contributeToActionBars();
    }

    private void hookContextMenu(){
        MenuManager menuMgr = new MenuManager("#PopupMenu");
        menuMgr.setRemoveAllWhenShown(true);
        menuMgr.addMenuListener(new IMenuListener(){
            public void menuAboutToShow(IMenuManager manager){
                TablesView.this.fillContextMenu(manager);
            }
        });
        Menu menu = menuMgr.createContextMenu(viewer.getControl());
        viewer.getControl().setMenu(menu);
        getSite().registerContextMenu(menuMgr, viewer);
    }

    private void contributeToActionBars(){
        IActionBars bars = getViewSite().getActionBars();
        fillLocalToolBar(bars.getToolBarManager());
    }

    private void fillContextMenu(IMenuManager manager){
        drillDownAdapter.addNavigationActions(manager);
        manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
    }

    private void fillLocalToolBar(IToolBarManager manager){
        manager.add(refresh);
        manager.add(new Separator());
        drillDownAdapter.addNavigationActions(manager);
    }

    private void makeActions(){
        refresh = new Action(){
            public void run(){
                viewer.refresh();
            }
        };
        refresh.setText("Refresh");
        refresh.setToolTipText("Refresh");
        refresh.setImageDescriptor(PlatformUI.getWorkbench().getSharedImages()
            .getImageDescriptor(ISharedImages.IMG_TOOL_REDO));
        doubleClickAction = new Action(){
            public void run(){
                ISelection selection = viewer.getSelection();
                TreeObject obj = (TreeObject)((IStructuredSelection)selection)
                    .getFirstElement();
                if(obj.getParent().getName().equals("Tables")
                    || obj.getParent().getName().equals("Views")){
                    IWorkbenchPage page = PlatformUI.getWorkbench()
                        .getActiveWorkbenchWindow().getActivePage();
                    try{
                        String name = obj.getName();
                        TreeParent parent = obj.getParent().getParent();
                        if(parent != null && !"".equals(parent.getName()))
                            name = parent.getName() + "." + name;

                        if(name.startsWith(userName + "."))
                            name = name.replaceAll("^" + userName + ".", "");
                        page.openEditor(new SQLEditorInput(name), SQLEditor.ID);
                    }catch(PartInitException e){
                        e.printStackTrace();
                    }
                }
            }
        };
    }

    private void hookDoubleClickAction(){
        viewer.addDoubleClickListener(new IDoubleClickListener(){
            public void doubleClick(DoubleClickEvent event){
                doubleClickAction.run();
            }
        });
    }

    public void setFocus(){
        viewer.getControl().setFocus();
    }
}
